// 仓颉语言 Tree-sitter 语法定义

const grammar = {
  name: 'cangjie',
  word: $ => /[\p{L}_][\p{L}\p{N}_]*/u,
  conflicts: $ => [
    [$.identifier, $.type_identifier],
  ],
  rules: {
    source_file: $ => repeat($._top_level_declaration),

    _top_level_declaration: $ => choice(
      $.package_declaration,
      $.import_declaration,
      $.function_declaration,
      $.class_declaration,
      $.struct_declaration,
      $.enum_declaration,
      $.interface_declaration,
      $.type_alias_declaration,
      $.constant_declaration,
      $.global_variable_declaration,
    ),

    package_declaration: $ => seq(
      'package',
      $.identifier,
      optional($._package_path),
      ';',
    ),

    _package_path: $ => seq('.', $.identifier),

    import_declaration: $ => seq(
      'import',
      choice(
        $.identifier,
        seq($.identifier, '.', '*'),
        seq($.identifier, optional($._package_path)),
        seq($.identifier, '.', $.identifier),
        seq('{', commaSep($.identifier), '}'),
      ),
      ';',
    ),

    function_declaration: $ => seq(
      optional($._visibility_modifier),
      optional($._function_modifier),
      'func',
      $.identifier,
      '(',
      optional(commaSep($.parameter_declaration)),
      ')',
      optional($._return_type),
      optional(seq('=', $._expression)),
      optional($.block),
    ),

    _visibility_modifier: $ => choice('public', 'private', 'protected'),

    _function_modifier: $ => choice('static', 'mut', 'async', 'override', 'open'),

    parameter_declaration: $ => seq(
      $.identifier,
      ':',
      $._type,
    ),

    _return_type: $ => seq(':', $._type),

    class_declaration: $ => seq(
      optional($._visibility_modifier),
      optional('open'),
      'class',
      $.identifier,
      optional(seq('<:', $.type_identifier)),
      optional($.class_body),
    ),

    class_body: $ => seq('{', repeat($._class_member), '}'),

    _class_member: $ => choice(
      $.field_declaration,
      $.method_declaration,
      $.constructor_declaration,
      $.property_declaration,
      $.constant_declaration,
    ),

    field_declaration: $ => seq(
      optional($._visibility_modifier),
      optional('mut'),
      'let',
      $.identifier,
      ':',
      $._type,
      optional(seq('=', $._expression)),
      ';',
    ),

    method_declaration: $ => $.function_declaration,

    constructor_declaration: $ => seq(
      optional($._visibility_modifier),
      'init',
      '(',
      optional(commaSep($.parameter_declaration)),
      ')',
      optional($.block),
    ),

    property_declaration: $ => seq(
      optional($._visibility_modifier),
      optional('mut'),
      'prop',
      $.identifier,
      ':',
      $._type,
      '{',
      optional(seq('get()', $._expression)),
      optional(seq('set(value:', $._type, ')', $.block)),
      '}',
    ),

    struct_declaration: $ => seq(
      optional($._visibility_modifier),
      'struct',
      $.identifier,
      optional($.struct_body),
    ),

    struct_body: $ => seq('{', repeat($._struct_member), '}'),

    _struct_member: $ => choice(
      $.field_declaration,
      $.method_declaration,
    ),

    enum_declaration: $ => seq(
      optional($._visibility_modifier),
      'enum',
      $.identifier,
      '{',
      optional(commaSep($.enum_variant)),
      '}',
    ),

    enum_variant: $ => seq(
      $.identifier,
      optional(seq('(', commaSep($._type), ')')),
    ),

    interface_declaration: $ => seq(
      optional($._visibility_modifier),
      'interface',
      $.identifier,
      '{',
      repeat($._interface_member),
      '}',
    ),

    _interface_member: $ => seq(
      optional($._visibility_modifier),
      'func',
      $.identifier,
      '(',
      optional(commaSep($.parameter_declaration)),
      ')',
      optional($._return_type),
      ';',
    ),

    type_alias_declaration: $ => seq(
      optional($._visibility_modifier),
      'type',
      $.type_identifier,
      '=',
      $._type,
      ';',
    ),

    constant_declaration: $ => seq(
      optional($._visibility_modifier),
      'const',
      $.identifier,
      '=',
      $._expression,
      ';',
    ),

    global_variable_declaration: $ => seq(
      optional($._visibility_modifier),
      choice('let', 'var'),
      $.identifier,
      optional(seq(':', $._type)),
      '=',
      $._expression,
      ';',
    ),

    block: $ => seq('{', repeat($._statement), '}'),

    _statement: $ => choice(
      $.variable_declaration,
      $.expression_statement,
      $.if_statement,
      $.for_statement,
      $.while_statement,
      $.match_statement,
      $.return_statement,
      $.break_statement,
      $.continue_statement,
      $.block,
    ),

    variable_declaration: $ => seq(
      choice('let', 'var'),
      $.identifier,
      optional(seq(':', $._type)),
      '=',
      $._expression,
      ';',
    ),

    expression_statement: $ => seq($._expression, ';'),

    if_statement: $ => seq(
      'if',
      '(',
      $._expression,
      ')',
      $.block,
      repeat(seq('else if', '(', $._expression, ')', $.block)),
      optional(seq('else', $.block)),
    ),

    for_statement: $ => seq(
      'for',
      '(',
      $.identifier,
      'in',
      $._expression,
      ')',
      $.block,
    ),

    while_statement: $ => seq(
      'while',
      '(',
      $._expression,
      ')',
      $.block,
    ),

    match_statement: $ => seq(
      'match',
      optional(seq('(', $._expression, ')')),
      '{',
      repeat($.match_case),
      '}',
    ),

    match_case: $ => seq(
      'case',
      $._pattern,
      optional(seq('where', $._expression)),
      '=>',
      choice($._expression, $.block),
    ),

    _pattern: $ => choice(
      $.identifier,
      $.number_literal,
      $.string_literal,
      $.boolean_literal,
      seq($.identifier, '(', commaSep($._pattern), ')'),
      seq('let', $.identifier),
      '_',
    ),

    return_statement: $ => seq('return', optional($._expression), ';'),

    break_statement: $ => seq('break', ';'),

    continue_statement: $ => seq('continue', ';'),

    _expression: $ => choice(
      $._literal,
      $.identifier,
      $.parenthesized_expression,
      $.binary_expression,
      $.unary_expression,
      $.call_expression,
      $.member_expression,
      $.lambda_expression,
      $.array_expression,
      $.struct_expression,
      $.enum_expression,
    ),

    _literal: $ => choice(
      $.number_literal,
      $.string_literal,
      $.boolean_literal,
      $.character_literal,
    ),

    parenthesized_expression: $ => seq('(', $._expression, ')'),

    binary_expression: $ => seq(
      $._expression,
      $._binary_operator,
      $._expression,
    ),

    _binary_operator: $ => choice(
      '+', '-', '*', '/', '%', '**',
      '==', '!=', '<', '>', '<=', '>=',
      '&&', '||',
      '=', '+=', '-=', '*=', '/=', '%=',
      'is', 'as',
    ),

    unary_expression: $ => seq($._unary_operator, $._expression),

    _unary_operator: $ => choice('!', '-', '++', '--'),

    call_expression: $ => seq(
      $._expression,
      '(',
      optional(commaSep($._expression)),
      ')',
    ),

    member_expression: $ => seq(
      $._expression,
      '.',
      $.identifier,
    ),

    lambda_expression: $ => seq(
      '{',
      optional(commaSep($.parameter_declaration)),
      '=>',
      $._expression,
      '}',
    ),

    array_expression: $ => seq(
      'Array',
      '(',
      $._expression,
      ',',
      $._expression,
      ')',
    ),

    struct_expression: $ => seq(
      $.type_identifier,
      '(',
      optional(commaSep($.named_argument)),
      ')',
    ),

    named_argument: $ => seq(
      $.identifier,
      '=',
      $._expression,
    ),

    enum_expression: $ => seq(
      $.type_identifier,
      '.',
      $.identifier,
      optional(seq('(', commaSep($._expression), ')')),
    ),

    _type: $ => choice(
      $.type_identifier,
      seq($.type_identifier, '<', commaSep($._type), '>'),
      seq('?', $._type),
      seq('(', commaSep($._type), ')', '->', $._type),
    ),

    type_identifier: $ => $.identifier,

    identifier: $ => $._name,
    _name: $ => $.word,

    number_literal: $ => /\d+(?:\.\d+)?(?:[eE][+-]?\d+)?(?:[uU][iI][nN][tT]?\d*)?/,
    string_literal: $ => seq(
      '"',
      repeat(choice(
        token.immediate(/[^"\\]/),
        $._escape_sequence,
      )),
      '"',
    ),
    boolean_literal: $ => choice('true', 'false'),
    character_literal: $ => seq(
      "'",
      choice(
        token.immediate(/[^'\\]/),
        $._escape_sequence,
      ),
      "'",
    ),
    _escape_sequence: $ => seq('\\', anychar),

    comment: $ => token(seq(
      '//',
      /.*/,
    )),
  },
};

function commaSep(rule) {
  return optional(seq(rule, repeat(seq(',', rule))));
}

module.exports = grammar;